<template>
  <div class="rich-editor flex flex-col" :class="$attrs.class">
    <!-- <div ref="toolbar">
      <select class="ql-size">
        <option value="12px">12px</option>
        <option value="14px" selected>14px</option>
        <option value="16px">16px</option>
        <option value="18px">18px</option>
      </select>

      <button class="ql-bold"></button>
    </div> -->
    <div ref="root"></div>
  </div>
  <Reference
    class="fixed"
    :style="poputStyle"
    ref="reference"
    trigger="manual"
    @change="handlePickParam"
  />
</template>
<script setup lang="ts">
import { CSSProperties, nextTick, onMounted, ref } from "vue";
import Quill from "quill";
import { SizeClass, SizeStyle } from "quill/formats/size";
// 样式
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
import { Delta } from "quill/core";

//
import Reference from "./reference.vue";
import { useMouse } from "@vueuse/core";

// 自定义blots
import { ReferenceBlock, ReferenceNode, ReferenceName } from "./blots";
import { attribute } from "./attributors.ts";

defineOptions({
  name: "RichEditor",
});

SizeClass.whitelist = ["12px", "14px", "16px", "18px"];
SizeStyle.whitelist = ["12px", "14px", "16px", "18px"];
// const SizeClass = new Parchment.ClassAttributor("size", "ql-size", {
//   scope: Parchment.Scope.INLINE,
//   whitelist: ["12px", "14px", "16px", "18px"],
// });
// const SizeStyle = new Parchment.StyleAttributor("size", "font-size", {
//   scope: Parchment.Scope.INLINE,
//   whitelist: ["12px", "14px", "16px", "18px"],
// });

Quill.register(attribute, true);
Quill.register(SizeStyle, true);
Quill.register(SizeClass, true);

const root = ref<HTMLElement>();
// const toolbar = ref<HTMLElement>();
let editor: Quill | null = null;
const reference = ref<typeof Reference>();
// 光标的位置
const cursorIndex = ref<number>(0);
const { x, y } = useMouse();
const poputStyle = ref<CSSProperties>({});

// 注册自定义块
Quill.register(ReferenceBlock);
Quill.register(ReferenceNode);
Quill.register(ReferenceName);

onMounted(() => {
  editor = new Quill(root.value!, {
    modules: {
      toolbar: [
        { font: [] },
        // { size: [] },
        { size: ["12px", false, "14px", "16px", "18px"] },
        "bold",
        "italic",
        { list: "ordered" },
        { list: "bullet" },
        { list: "check" },
        { align: [] },
        { background: [] },
        { color: [] },

        "table",
      ],
    },
    // modules: {
    //   toolbar: {
    //     container: toolbar.value!,
    //   },
    // },
    formats: null,
    theme: "snow",
  });
  // editor!.format("size", "12px");

  // 手动格式化文本
  const delta = formatContent("你好啊 {{001.name}} nic{");
  // 插入文本内容
  editor.setContents(delta);
  // 监听值变化
  editor.on(
    "text-change",
    (delta: Delta, _oldContents: Delta, source: string) => {
      reference.value?.close();
      getContent();
      // 用户操作
      if (source !== "user") return;

      // 鼠标位置
      const range = editor!.getSelection()!;
      console.log(range);
      for (const op of delta.ops) {
        if (typeof op.insert == "string" && ["/", "{"].includes(op.insert)) {
          handleShowReference();
          // 存储当前光标位置
          cursorIndex.value = range.index;
        }
      }
    }
  );
});

function formatContent(content: string): Delta {
  const len = content.length;

  const delta = new Delta();

  let i = 0;
  while (i < len) {
    let text = "";
    while (
      i < len &&
      (content[i] != "{" || i == len - 1 || content[i + 1] != "{")
    ) {
      text += content[i];
      i++;
    }
    delta.insert(text);
    // 自定义
    if (i >= len) {
      break;
    }
    let j = i + 2;
    let template = "";
    while (content[j] != "}") {
      template += content[j];
      j++;
    }
    const keys = template.split(".");
    delta.insert({
      "reference-block": {
        id: keys[0],
        name: keys.slice(1).join("."),
        label: "开始",
      },
    });
    i = j + 2;
  }

  return delta;
}
function getContent() {
  const contents = editor!.getContents();

  let template = "";
  for (const op of contents.ops) {
    if (!op.insert) {
      continue;
    }
    if (typeof op.insert == "object" && op.insert["reference-block"]) {
      const { id, name } = op.insert["reference-block"] as {
        id: string;
        name: string;
      };
      template += `{{${id}.${name}}}`;
    } else {
      template += op.insert;
    }
  }
}
function handleShowReference() {
  let left = x.value;
  let top = y.value;

  poputStyle.value = {
    left: left + "px",
    top: top + "px",
  };
  nextTick(() => {
    reference.value?.show();
  });
}
/**
 * 选择参数
 * @param param0
 */
const handlePickParam = ({
  nodeId,
  name,
}: {
  nodeId: string;
  name: string;
}) => {
  reference.value?.close();

  // 插入的数据模板
  const template = `{{${nodeId}.${name}}}`;

  // 删掉之前作为触发的一个字符
  //
  // cursorIndex.value -= 1;
  // if (cursorIndex.value < 0) cursorIndex.value = 0;
  // editor!.deleteText(cursorIndex.value, 1);

  cursorIndex.value -= 1;
  // 第一行第一个字符光标位置 hack
  if (cursorIndex.value < 0) cursorIndex.value = 0;
  // 其他行第一个字符光标位置 hack
  if (editor!.getText(cursorIndex.value).startsWith("\n")) {
    cursorIndex.value += 1;
  }
  // 插入数据，光标位置为存储的光标位置
  // editor!.insertText(cursorIndex.value, template);

  // const span = editor!.scroll.create(ReferenceNode.blotName, "33");

  let delta = editor!.insertEmbed(cursorIndex.value, "reference-block", {
    id: nodeId,
    name,
    label: "开始",
  });
  // cursorIndex.value += template.length;
  cursorIndex.value += 1;
  editor!.deleteText(cursorIndex.value, 1);

  // editor!.setSelection(cursorIndex.value - 1, 1);
  // editor!.format("size", "12px");
  // editor!.format("bold", true);
  // editor!.format("hboot-width", 500);

  // 移动光标到后一个位置
  editor!.setSelection(cursorIndex.value, Quill.sources.API);
};
</script>
<style lang="less" scoped>
.rich-editor {
  :deep(.ql-snow) {
    @font-sizes: 12px, 14px, 16px, 18px;
    // 默认
    @font-default: "Normal";

    .generate-font-size(@sizes,@set-font:false) {
      each(@sizes,{
        @value: extract(@sizes,@index);
        &[data-value="@{value}"]::before{
          content:"@{value}"
        } 
        &[data-value="@{value}"]::before when (@set-font){
          font-size:@value;
        }
      });
    }
    .reference-block {
      display: inline-flex;
      // background-color: #ddd;
      border-radius: 5px;
      padding: 3px;
      font-size: 12px;
      user-select: none;
      cursor: pointer;
      margin: 0 3px;
      box-shadow: 0 1px 3px 0 #bbb;
    }
    .ql-editor {
      each(@font-sizes,{
        @value: extract(@font-sizes,@index);

        .ql-size-@{value} {
          font-size: @value;
        }
      });
    }
    .ql-picker.ql-size {
      .ql-picker-label,
      .ql-picker-item {
        &::before {
          content: @font-default;
        }
      }
      .ql-picker-label {
        .generate-font-size( @font-sizes);
      }
      .ql-picker-item {
        .generate-font-size( @font-sizes,true);
      }
    }

    //   .ql-editor {
    //     .ql-size-12px {
    //       font-size: 12px;
    //     }
    //     .ql-size-14px {
    //       font-size: 14px;
    //     }
    //     .ql-size-16px {
    //       font-size: 16px;
    //     }
    //     .ql-size-18px {
    //       font-size: 18px;
    //     }
    //   }
    //   .ql-picker.ql-size {
    //     .ql-picker-label {
    //       &::before {
    //         content: "Normal";
    //       }
    //       &[data-value="12px"]::before {
    //         content: "12px";
    //       }
    //       &[data-value="14px"]::before {
    //         content: "14px";
    //       }
    //       &[data-value="16px"]::before {
    //         content: "16px";
    //       }
    //       &[data-value="18px"]::before {
    //         content: "18px";
    //       }
    //     }
    //     .ql-picker-item {
    //       &::before {
    //         content: "Normal";
    //       }
    //       &[data-value="12px"]::before {
    //         content: "12px";
    //         font-size: 12px;
    //       }
    //       &[data-value="14px"]::before {
    //         content: "14px";
    //         font-size: 14px;
    //       }
    //       &[data-value="16px"]::before {
    //         content: "16px";
    //         font-size: 16px;
    //       }
    //       &[data-value="18px"]::before {
    //         content: "18px";
    //         font-size: 18px;
    //       }
    //     }
    //   }
  }
}
</style>
